package dao

import (
	"context"
	"gorm.io/gorm"
	"mychat/internal/common/logger"
	"mychat/internal/data/dao/model"
)

type UserDao interface {
	CreateUser(ctx context.Context, user *model.User) error
	GetUser(ctx context.Context, userId uint) (*model.User, error)
	GetUserWithName(ctx context.Context, name string) (*model.User, error)
	DeleteUser(ctx context.Context, user *model.User) error
	ListUserWithParam(ctx context.Context, param *model.ListUserParam) (int64, []*model.User, error)
	ListUserWithIds(ctx context.Context, userIds []uint) ([]*model.User, error)
}

type userDao struct {
	db *gorm.DB
	lg logger.Logger
}

func NewUserDao(db *gorm.DB, lg logger.Logger) UserDao {
	return &userDao{
		db: db,
		lg: lg.WithCustomTag(logger.LoggerTagMySQL),
	}
}

func (d *userDao) CreateUser(ctx context.Context, user *model.User) error {
	db := d.db.WithContext(ctx)
	err := db.Create(user).Error
	if err != nil {
		return err
	}
	return nil
}

func (d *userDao) GetUser(ctx context.Context, userId uint) (*model.User, error) {
	db := d.db.WithContext(ctx)
	var user model.User
	err := db.Table(user.TableName()).
		Where("id = ?", userId).
		First(&user).
		Error

	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return &user, nil
}

func (d *userDao) GetUserWithName(ctx context.Context, name string) (*model.User, error) {
	db := d.db.WithContext(ctx)
	var user model.User
	err := db.Table(user.TableName()).
		Where("name = ?", name).
		First(&user).
		Error

	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}

	return &user, nil
}

func (d *userDao) DeleteUser(ctx context.Context, user *model.User) error {
	db := d.db.WithContext(ctx)
	err := db.Delete(user).Error
	if err != nil {
		return err
	}
	return nil
}

func (d *userDao) ListUserWithParam(ctx context.Context, param *model.ListUserParam) (int64, []*model.User, error) {
	db := d.db.WithContext(ctx)
	db = db.Table(model.User{}.TableName()).
		Where("name like ?", "%"+param.Keyword+"%")

	var total int64
	err := db.Count(&total).Error
	if err != nil {
		return 0, nil, err
	}

	var offset int
	if param.PageIndex > 1 {
		offset = (param.PageIndex - 1) * param.PageSize
	}

	var order = "id asc"
	if param.Order != "" {
		order = param.Order
	}
	var users []*model.User
	err = db.Offset(offset).Limit(param.PageSize).
		Order(order).Find(&users).Error
	if err != nil {
		return 0, nil, err
	}
	return total, users, nil
}

func (d *userDao) ListUserWithIds(ctx context.Context, userIds []uint) ([]*model.User, error) {
	db := d.db.WithContext(ctx)
	var users []*model.User
	err := db.Table(model.User{}.TableName()).
		Where("id in ?", userIds).
		Find(&users).
		Error
	if err != nil {
		return nil, err
	}
	return users, nil
}
